from PySide6.QtWidgets import (QGraphicsView, QGraphicsScene, QMenu,
                               QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
                               QLabel, QDoubleSpinBox, QComboBox, QLineEdit,
                               QFileDialog, QMessageBox, QGraphicsPathItem)
from PySide6.QtCore import Qt, QPointF, QMimeData
from PySide6.QtGui import QAction, QKeyEvent, QBrush, QColor, QPainter, QPen, QPainterPath, QDragEnterEvent, QDropEvent
import uuid

from .node import AudioNode, PortItem
from .connection import ConnectionItem


class TempConnectionItem(QGraphicsPathItem):
    """临时连接项，用于拖动过程中的预览"""

    def __init__(self, startPort, startPos):
        super().__init__()
        self.startPort = startPort
        self.startPos = startPos
        self.endPos = startPos

        # 临时连接使用虚线样式
        self.setPen(QPen(QColor(200, 200, 100, 180), 2, Qt.DashLine))
        self.updatePath()

    def updateEndPos(self, endPos):
        """更新终点位置"""
        self.endPos = endPos
        self.updatePath()

    def updatePath(self):
        """更新连接路径"""
        path = QPainterPath()
        path.moveTo(self.startPos)

        # 计算控制点
        dx = (self.endPos.x() - self.startPos.x()) * 0.5
        control1 = QPointF(self.startPos.x() + dx, self.startPos.y())
        control2 = QPointF(self.endPos.x() - dx, self.endPos.y())

        path.cubicTo(control1, control2, self.endPos)
        self.setPath(path)


class AudioCanvas(QGraphicsView):
    def __init__(self, apsParser):
        super().__init__()
        self.apsParser = apsParser
        self.scene = QGraphicsScene()
        self.setScene(self.scene)

        # 修复渲染提示设置
        self.setRenderHint(QPainter.Antialiasing)
        self.setDragMode(self.DragMode.RubberBandDrag)
        self.setAcceptDrops(True)  # 启用拖放接受

        # 状态变量
        self.tempConnection = None
        self.draggingPort = None
        self.copiedNodesData = None
        self.cutNodesData = None
        self.scaleFactor = 1.0
        self.currentTheme = "dark"  # 添加当前主题变量

        self.setupScene()

    def setTheme(self, theme):
        """设置画布主题"""
        self.currentTheme = theme
        # 更新现有节点的主题 - 添加错误处理
        for item in self.scene.items():
            try:
                if hasattr(item, 'updateTheme'):
                    item.updateTheme(theme)
            except Exception as e:
                print(f"更新项目主题时出错: {e}")
                # 继续处理其他项目，不要因为一个项目失败而停止

    def setupScene(self):
        """设置场景背景"""
        # 根据当前主题设置背景
        if self.currentTheme == "light":
            self.scene.setBackgroundBrush(QBrush(QColor(240, 240, 240)))
        else:
            self.scene.setBackgroundBrush(QBrush(QColor(40, 40, 40)))

    def setBackgroundColor(self, color):
        """设置画布背景颜色"""
        self.scene.setBackgroundBrush(QBrush(color))

    def dragEnterEvent(self, event):
        """拖拽进入事件"""
        if event.mimeData().hasFormat("application/x-audionode"):
            event.acceptProposedAction()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        """拖拽移动事件"""
        if event.mimeData().hasFormat("application/x-audionode"):
            event.acceptProposedAction()
        else:
            event.ignore()

    def dropEvent(self, event):
        """拖放事件"""
        if event.mimeData().hasFormat("application/x-audionode"):
            # 获取拖拽的数据
            mimeData = event.mimeData()
            nodeType = mimeData.data("application/x-audionode").data().decode('utf-8')

            # 获取放置位置
            scenePos = self.mapToScene(event.pos())

            # 添加节点，传入当前主题
            self.addNode(nodeType, scenePos)

            event.acceptProposedAction()
        else:
            event.ignore()

    def getNodeById(self, nodeId):
        """根据ID获取节点"""
        if not self.scene:
            return None
        for item in self.scene.items():
            if isinstance(item, AudioNode) and item.nodeId == nodeId:
                return item
        return None

    def getAllNodes(self):
        """获取所有节点"""
        if not self.scene:
            return []
        nodes = []
        for item in self.scene.items():
            if isinstance(item, AudioNode):
                nodes.append(item)
        return nodes

    def getSelectedNodes(self):
        """获取选中的节点"""
        selectedNodes = []
        for item in self.scene.selectedItems():
            if isinstance(item, AudioNode):
                selectedNodes.append(item)
        return selectedNodes

    def getAllConnections(self):
        """获取所有连接"""
        if not self.scene:
            return []
        connections = []
        for item in self.scene.items():
            if isinstance(item, ConnectionItem):
                connections.append(item)
        return connections

    def clearCanvas(self):
        """清空画布"""
        if self.scene:
            self.scene.clear()
        self.tempConnection = None
        self.draggingPort = None

    def addNode(self, nodeType, position, nodeId=None, parameters=None, customName=None):
        """添加节点到画布"""
        nodeData = self.apsParser.getSubSystem(nodeType)
        if nodeData:
            node = AudioNode(nodeData, position, nodeId)
            self.scene.addItem(node)

            # 设置参数值
            if parameters:
                for paramName, value in parameters.items():
                    node.setParameterValue(paramName, value)

            # 设置自定义名称
            if customName:
                node.setNodeName(customName)

            # 根据当前主题更新节点外观
            try:
                if hasattr(node, 'updateTheme'):
                    node.updateTheme(self.currentTheme)
            except Exception as e:
                print(f"更新节点主题时出错: {e}")

            print(f"已添加节点: {node.getDisplayName()} 在位置 ({position.x():.1f}, {position.y():.1f})")
            return node
        else:
            print(f"错误: 未知的节点类型: {nodeType}")
        return None

    def createConnection(self, startNodeId, startPortName, endNodeId, endPortName):
        """根据节点ID和端口名称创建连接"""
        startNode = self.getNodeById(startNodeId)
        endNode = self.getNodeById(endNodeId)

        if not startNode or not endNode:
            print(f"错误: 无法找到节点 - 开始节点: {startNodeId}, 结束节点: {endNodeId}")
            return None

        startPort = startNode.getOutputPort(startPortName)
        endPort = endNode.getInputPort(endPortName)

        if not startPort or not endPort:
            print(f"错误: 无法找到端口 - 开始端口: {startPortName}, 结束端口: {endPortName}")
            return None

        # 创建连接
        connection = ConnectionItem(startPort, endPort)
        if connection.scene():  # 如果连接已经添加到场景
            return connection
        else:
            # 如果连接没有自动添加到场景，手动添加
            self.scene.addItem(connection)
            return connection

    def getSerializationData(self):
        """获取画布序列化数据"""
        nodesData = []
        connectionsData = []

        # 序列化所有节点
        for node in self.getAllNodes():
            nodesData.append(node.getSerializationData())

        # 序列化所有连接
        for connection in self.getAllConnections():
            connectionsData.append(connection.getSerializationData())

        return {
            'nodes': nodesData,
            'connections': connectionsData
        }

    def loadFromSerializationData(self, data):
        """从序列化数据加载画布"""
        self.clearCanvas()

        # 先创建所有节点
        nodeMap = {}
        for nodeData in data.get('nodes', []):
            node = self.addNode(
                nodeData['nodeType'],
                QPointF(nodeData['position']['x'], nodeData['position']['y']),
                nodeData['nodeId'],
                nodeData.get('parameters', {}),
                nodeData.get('customName')  # 添加自定义名称
            )
            if node:
                nodeMap[nodeData['nodeId']] = node

        # 然后创建所有连接
        for connectionData in data.get('connections', []):
            self.createConnection(
                connectionData['startNodeId'],
                connectionData['startPortName'],
                connectionData['endNodeId'],
                connectionData['endPortName']
            )

        return True

    def getSelectedContentData(self):
        """获取选中内容的序列化数据"""
        selectedNodes = self.getSelectedNodes()
        if not selectedNodes:
            return None

        # 获取选中节点的ID集合
        selectedNodeIds = {node.nodeId for node in selectedNodes}

        # 序列化选中的节点
        nodesData = []
        for node in selectedNodes:
            nodesData.append(node.getSerializationData())

        # 序列化选中的节点之间的连接
        connectionsData = []
        for connection in self.getAllConnections():
            # 只保留两个端点都在选中节点中的连接
            if (connection.startPort.parentNode.nodeId in selectedNodeIds and
                    connection.endPort.parentNode.nodeId in selectedNodeIds):
                connectionsData.append(connection.getSerializationData())

        return {
            'nodes': nodesData,
            'connections': connectionsData
        }

    def pasteContentData(self, contentData, position):
        """将内容数据粘贴到指定位置"""
        if not contentData or 'nodes' not in contentData:
            return False

        # 计算选中节点的边界框，用于确定粘贴位置
        nodesData = contentData.get('nodes', [])
        if not nodesData:
            return False

        # 计算原始内容的中心点
        minX = min(node['position']['x'] for node in nodesData)
        maxX = max(node['position']['x'] for node in nodesData)
        minY = min(node['position']['y'] for node in nodesData)
        maxY = max(node['position']['y'] for node in nodesData)
        centerX = (minX + maxX) / 2
        centerY = (minY + maxY) / 2

        # 计算偏移量
        offsetX = position.x() - centerX
        offsetY = position.y() - centerY

        # 创建新节点ID映射表
        newNodeMap = {}

        # 先创建所有节点
        for nodeData in nodesData:
            # 生成新的节点ID
            newNodeId = str(uuid.uuid4())

            # 创建新节点
            newNode = self.addNode(
                nodeData['nodeType'],
                QPointF(nodeData['position']['x'] + offsetX, nodeData['position']['y'] + offsetY),
                newNodeId,
                nodeData.get('parameters', {})
            )

            if newNode:
                # 保存新旧节点ID映射
                newNodeMap[nodeData['nodeId']] = newNode

        # 然后创建所有连接
        connectionsData = contentData.get('connections', [])
        for connectionData in connectionsData:
            # 获取新的节点ID
            startNodeId = connectionData['startNodeId']
            endNodeId = connectionData['endNodeId']

            if startNodeId in newNodeMap and endNodeId in newNodeMap:
                # 创建连接
                self.createConnection(
                    newNodeMap[startNodeId].nodeId,
                    connectionData['startPortName'],
                    newNodeMap[endNodeId].nodeId,
                    connectionData['endPortName']
                )

        print(f"已粘贴 {len(nodesData)} 个节点和 {len(connectionsData)} 个连接")
        return True

    def contextMenuEvent(self, event):
        """画布右键菜单"""
        menu = QMenu(self)

        # 获取选中的节点
        selectedNodes = self.getSelectedNodes()

        # 复制菜单项
        copyAction = QAction("复制", menu)
        copyAction.setShortcut('Ctrl+C')
        copyAction.triggered.connect(self.copySelectedContent)
        copyAction.setEnabled(len(selectedNodes) > 0)
        menu.addAction(copyAction)

        # 剪切菜单项
        cutAction = QAction("剪切", menu)
        cutAction.setShortcut('Ctrl+X')
        cutAction.triggered.connect(self.cutSelectedContent)
        cutAction.setEnabled(len(selectedNodes) > 0)
        menu.addAction(cutAction)

        # 粘贴菜单项
        pasteAction = QAction("粘贴", menu)
        pasteAction.setShortcut('Ctrl+V')
        pasteAction.triggered.connect(lambda: self.pasteContent(self.mapToScene(event.pos())))
        pasteAction.setEnabled(self.copiedNodesData is not None or self.cutNodesData is not None)
        menu.addAction(pasteAction)

        # 删除菜单项
        if selectedNodes:
            menu.addSeparator()
            deleteAction = QAction("删除", menu)
            deleteAction.setShortcut('Del')
            deleteAction.triggered.connect(self.deleteSelectedContent)
            menu.addAction(deleteAction)

        menu.addSeparator()

        # 创建子系统子菜单
        createMenu = QMenu("新建", menu)

        # 获取子系统列表
        subSystems = self.apsParser.getAllSubSystems()

        for systemType, systemData in subSystems.items():
            action = QAction(systemData.get('name', systemType), createMenu)
            action.triggered.connect(
                lambda checked=False, st=systemType, pos=event.pos():
                self.addNode(st, self.mapToScene(pos))
            )
            createMenu.addAction(action)

        menu.addMenu(createMenu)

        menu.exec_(event.globalPos())

    def copySelectedContent(self):
        """复制选中的内容"""
        contentData = self.getSelectedContentData()
        if contentData:
            self.copiedNodesData = contentData
            self.cutNodesData = None  # 清除剪切数据
            print(f"已复制 {len(contentData['nodes'])} 个节点和 {len(contentData['connections'])} 个连接")

    def cutSelectedContent(self):
        """剪切选中的内容"""
        contentData = self.getSelectedContentData()
        if contentData:
            self.cutNodesData = contentData
            self.copiedNodesData = None  # 清除复制数据

            # 删除选中的内容
            self.deleteSelectedContent()

            print(f"已剪切 {len(contentData['nodes'])} 个节点和 {len(contentData['connections'])} 个连接")

    def pasteContent(self, position):
        """粘贴内容到指定位置"""
        if self.copiedNodesData:
            # 粘贴复制的节点
            self.pasteContentData(self.copiedNodesData, position)
        elif self.cutNodesData:
            # 粘贴剪切的节点
            self.pasteContentData(self.cutNodesData, position)
            # 清除剪切数据
            self.cutNodesData = None

    def deleteSelectedContent(self):
        """删除选中的内容"""
        selectedNodes = self.getSelectedNodes()
        for node in selectedNodes:
            self.deleteNode(node)
        print(f"已删除 {len(selectedNodes)} 个节点")

    def keyPressEvent(self, event):
        """键盘事件处理"""
        if event.key() == Qt.Key_C and event.modifiers() == Qt.ControlModifier:
            # Ctrl+C 复制
            self.copySelectedContent()
            event.accept()
        elif event.key() == Qt.Key_X and event.modifiers() == Qt.ControlModifier:
            # Ctrl+X 剪切
            self.cutSelectedContent()
            event.accept()
        elif event.key() == Qt.Key_V and event.modifiers() == Qt.ControlModifier:
            # Ctrl+V 粘贴
            viewportCenter = self.viewport().rect().center()
            scenePos = self.mapToScene(viewportCenter)
            self.pasteContent(scenePos)
            event.accept()
        elif event.key() == Qt.Key_Delete:
            # Delete 删除
            self.deleteSelectedContent()
            event.accept()
        else:
            super().keyPressEvent(event)

    def mousePressEvent(self, event):
        """鼠标点击事件处理"""
        if event.button() == Qt.LeftButton:
            item = self.itemAt(event.pos())
            if isinstance(item, PortItem):
                self.draggingPort = item
                scenePos = self.mapToScene(event.pos())

                # 创建临时连接预览
                self.tempConnection = TempConnectionItem(item, scenePos)
                self.scene.addItem(self.tempConnection)

                # 阻止默认的拖拽行为
                event.accept()
                return
            else:
                super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        """鼠标移动事件处理"""
        if self.tempConnection and self.draggingPort:
            # 更新临时连接的终点位置
            scenePos = self.mapToScene(event.pos())
            self.tempConnection.updateEndPos(scenePos)
            event.accept()
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        """鼠标释放事件处理"""
        if self.tempConnection and self.draggingPort and event.button() == Qt.LeftButton:
            scenePos = self.mapToScene(event.pos())

            # 使用场景坐标查找目标项
            items = self.scene.items(scenePos)
            targetPort = None

            # 查找第一个端口项
            for item in items:
                if isinstance(item, PortItem):
                    targetPort = item
                    break

            # 检查是否可以创建连接
            if (targetPort is not None and
                    targetPort.isInput != self.draggingPort.isInput and
                    targetPort.parentNode != self.draggingPort.parentNode):

                # 确定正确的连接方向
                if self.draggingPort.isInput:
                    # 如果拖拽的是输入端口，那么目标应该是输出端口
                    startPort = targetPort
                    endPort = self.draggingPort
                else:
                    # 如果拖拽的是输出端口，那么目标应该是输入端口
                    startPort = self.draggingPort
                    endPort = targetPort

                # 确保连接是从输出到输入
                if not startPort.isInput and endPort.isInput:
                    # 创建连接
                    connection = ConnectionItem(startPort, endPort)
                    if connection.scene():  # 检查连接是否成功添加到场景
                        print(
                            f"成功创建连接: {startPort.parentNode.nodeData['name']}.{startPort.portName} -> {endPort.parentNode.nodeData['name']}.{endPort.portName}")
                    else:
                        # 如果连接没有自动添加到场景，手动添加
                        self.scene.addItem(connection)
                        print(
                            f"手动添加连接: {startPort.parentNode.nodeData['name']}.{startPort.portName} -> {endPort.parentNode.nodeData['name']}.{endPort.portName}")
                else:
                    print("错误: 连接方向不正确")
            else:
                if targetPort is None:
                    print("未找到目标端口")
                elif targetPort.isInput == self.draggingPort.isInput:
                    print(f"端口类型相同: 都是{'输入' if self.draggingPort.isInput else '输出'}端口")
                elif targetPort.parentNode == self.draggingPort.parentNode:
                    print("不能连接到同一个节点")

            # 清理临时连接
            self.scene.removeItem(self.tempConnection)
            self.tempConnection = None
            self.draggingPort = None

            event.accept()
        else:
            super().mouseReleaseEvent(event)

    def copyNode(self, node):
        """复制单个节点（向后兼容）"""
        self.copiedNodesData = {
            'nodes': [node.getSerializationData()],
            'connections': []
        }
        self.cutNodesData = None

    def cutNode(self, node):
        """剪切单个节点（向后兼容）"""
        self.cutNodesData = {
            'nodes': [node.getSerializationData()],
            'connections': []
        }
        self.copiedNodesData = None
        self.deleteNode(node)

    def pasteNode(self, position):
        """粘贴单个节点（向后兼容）"""
        if self.copiedNodesData:
            self.pasteContentData(self.copiedNodesData, position)
        elif self.cutNodesData:
            self.pasteContentData(self.cutNodesData, position)
            self.cutNodesData = None

    def deleteNode(self, node):
        """删除节点"""
        # 删除所有连接
        for port in list(node.inputPorts.values()) + list(node.outputPorts.values()):
            for connection in port.connections[:]:
                connection.delete()

        self.scene.removeItem(node)

    def showNodeSettings(self, node):
        """显示节点设置（由主窗口处理）"""
        if hasattr(self, 'parent') and self.parent():
            self.parent().showNodeSettings(node)

    def wheelEvent(self, event):
        """滚轮缩放"""
        if event.modifiers() & Qt.ControlModifier:
            zoomIn = event.angleDelta().y() > 0
            factor = 1.1 if zoomIn else 0.9
            self.scale(factor, factor)
            self.scaleFactor *= factor
            event.accept()
        else:
            super().wheelEvent(event)

    def renameNode(self, node, new_name):
        """重命名节点"""
        if node and new_name:
            node.setNodeName(new_name)
            self.markUnsavedChanges()

    def markUnsavedChanges(self):
        """标记有未保存的更改"""
        # 使用窗口层级找到主窗口
        try:
            main_window = self.window()
            if hasattr(main_window, 'markUnsavedChanges'):
                main_window.markUnsavedChanges()
        except Exception as e:
            print(f"标记未保存更改时出错: {e}")

    def renameNode(self, node, new_name):
        """重命名节点"""
        if node and new_name:
            node.setNodeName(new_name)
            self.markUnsavedChanges()

    def updateNodeNameInSettings(self, nodeId, new_name):
        """更新设置面板中的节点名称显示"""
        if hasattr(self, 'parent') and self.parent():
            main_window = self.parent()
            if hasattr(main_window, 'settingsPanel'):
                settings_panel = main_window.settingsPanel
                # 如果当前显示的节点是正在重命名的节点，则更新名称显示
                if (hasattr(settings_panel, 'currentNode') and
                        settings_panel.currentNode and
                        settings_panel.currentNode.nodeId == nodeId):
                    # 暂时断开信号以避免循环
                    settings_panel.nodeNameEdit.blockSignals(True)
                    settings_panel.nodeNameEdit.setText(new_name)
                    settings_panel.nodeNameEdit.blockSignals(False)

    def validate_connections(self):
        """验证所有连接的完整性"""
        errors = []
        warnings = []

        nodes = self.getAllNodes()
        connections = self.getAllConnections()

        # 检查波形查看器的输入连接
        for node in nodes:
            if hasattr(node, 'nodeType') and node.nodeType == 'waveform_viewer':
                if not node.inputPorts or not any(port.connections for port in node.inputPorts.values()):
                    warnings.append(f"波形查看器 '{node.getDisplayName()}' 没有输入连接")

        # 检查输出节点的输入连接
        for node in nodes:
            if hasattr(node, 'nodeType') and node.nodeType == 'output':
                if not node.inputPorts or not any(port.connections for port in node.inputPorts.values()):
                    errors.append(f"输出节点 '{node.getDisplayName()}' 没有输入连接")

        # 检查输入节点的输出连接
        for node in nodes:
            if hasattr(node, 'nodeType') and node.nodeType == 'input':
                if not node.outputPorts or not any(port.connections for port in node.outputPorts.values()):
                    warnings.append(f"输入节点 '{node.getDisplayName()}' 没有输出连接")

        # 显示验证结果
        if errors or warnings:
            self.show_connection_validation_dialog(errors, warnings)

        return len(errors) == 0

    def show_connection_validation_dialog(self, errors, warnings):
        """显示连接验证对话框"""
        try:
            from PySide6.QtWidgets import QMessageBox

            message = ""
            if errors:
                message += "❌ 错误:\n" + "\n".join(f"• {error}" for error in errors) + "\n\n"
            if warnings:
                message += "⚠️ 警告:\n" + "\n".join(f"• {warning}" for warning in warnings)

            if not message:
                message = "所有连接验证通过！"

            msg = QMessageBox()
            if errors:
                msg.setIcon(QMessageBox.Critical)
                msg.setWindowTitle("连接验证 - 错误")
            elif warnings:
                msg.setIcon(QMessageBox.Warning)
                msg.setWindowTitle("连接验证 - 警告")
            else:
                msg.setIcon(QMessageBox.Information)
                msg.setWindowTitle("连接验证 - 通过")

            msg.setText(message)
            msg.setStandardButtons(QMessageBox.Ok)
            msg.exec_()

        except Exception as e:
            print(f"显示验证对话框时出错: {e}")

